#include "image.h"
#include "types.h"
#include "externtypes.h"
#include <cstdlib>

#include <gtkmm.h>

#ifndef NULL
#define NULL 0
#endif
//////////
//Image methods
//////////

void Image::setPixelSize(float meters){
    pixelsize=meters;
}

float Image::getPixelSize() const{
    return pixelsize;
}

void Image::setHeight(unsigned int h){
    height=h;
}

unsigned int Image::getHeight() const{
    return height;
}

void Image::setWidth(unsigned int w){
    width=w;
}

unsigned int Image::getWidth() const{
    return width;
}

void Image::invert(){}

ImageType Image::getType(){
    return type;
}

void Image::setType(ImageType type){
    this->type=type;
}
/*
void Image::saveToFile(string filename,string type){
    Glib::RefPtr<Gdk::Pixbuf>* gtkimage;
    gtkimage=imagetoPixbuf(this);
    (*gtkimage)->save(filename,type);
    delete gtkimage;
}

Color_Image* Image::openFromFile(string path){
    Gtk::Window* one;
    Gtk::Main::run(*one);
    Color_Image *out;
    Glib::RefPtr<Gdk::Pixbuf> *interfaceimage = new Glib::RefPtr<Gdk::Pixbuf>;
    *interfaceimage = Gdk::Pixbuf::create_from_file(path);
    out=pixbuftoColor(interfaceimage);
    delete interfaceimage;
    return out;
}
*/
Image* Image::copy(Image* imageout){
    switch(type){
        case BINARY:
            return ((Binary_Image*)this)->binarycopy((Binary_Image*)imageout);
        case GREY:
            return ((Grey_Image*)this)->greycopy((Grey_Image*)imageout);
        case COLOR:
            return ((Color_Image*)this)->colorcopy((Color_Image*)imageout);
        default:
            return NULL;
    }
}

Image::~Image(){}

//////////
//Grey_Image methods
//////////

Grey_Image::Grey_Image(int height,int width,unsigned int levels = 256){

    //set Grey_image propriety:
    setHeight(height);
    setWidth(width);
    this->levels = levels;
    setPixelSize(SIZE_NOT_DEFINED);
    setType(Image::GREY);

    //Allocate pixelmap:
    pixelmap = new unsigned int*[height];
    for(int i=0;i<height;i++)
        pixelmap[i]=new unsigned int[width];
    if(pixelmap[height-1]==NULL)exit(13)/*Must be swapped by correct "out of memory" function/exception*/;

    //Zero pixmap:
    for(int i=0;i < height;i++)
        for(int j=0;j < width;j++)
            pixelmap[i][j]=0;

}

Grey_Image::~Grey_Image(){
    //Desallocate pixelmap:
    for(unsigned int i=0;i < getHeight() ;i++)
        delete[] pixelmap[i];

    delete[] pixelmap;

}

void Grey_Image::setPixel(Position position,unsigned int value){
    if(position.isInside(-1,-1,getWidth(),getHeight()))
        pixelmap[position.y][position.x]=value;
}
void Grey_Image::setPixel(int xposition,int yposition,unsigned int value){
    if(xposition<(int)getWidth() && yposition<(int)getHeight()
      && xposition>=0 && yposition >=0)
        pixelmap[yposition][xposition]=value;
}

unsigned int Grey_Image::getPixel(Position position){
    if(position.isInside(-1,-1,getWidth(),getHeight()))
        return pixelmap[position.y][position.x];
    return 0;
}
unsigned int Grey_Image::getPixel(int xposition,int yposition){
    if(xposition<(int)getWidth() && yposition<(int)getHeight()
       && xposition>=0 && yposition >=0)
        return pixelmap[yposition][xposition];
    return 0;
}

void Grey_Image::setGreyLevels(unsigned int l){
    levels=l;
}

unsigned int Grey_Image::getGreyLevels() const{
    return levels;
}

void Grey_Image::invert(){
    for(unsigned int i=0;i<getHeight();i++)
        for(unsigned int j=0;j<getWidth();j++){
            pixelmap[i][j]=(levels-1)-pixelmap[i][j];
        }
}

Image* Grey_Image::convert(ImageType newtype,int option,unsigned int value){
    if(newtype!=getType()){
        if(newtype==BINARY){
            Binary_Image *out = new Binary_Image(getHeight(),getWidth());
            switch(option){
                case 0:
                    for(unsigned int i=0;i<getHeight();i++)
                        for(unsigned int j=0;j<getWidth();j++)
                            if(getPixel(j,i)==value)
                                out->pixelmap[i][j]=true;
                    break;
                case 1:
                    for(unsigned int i=0;i<getHeight();i++)
                        for(unsigned int j=0;j<getWidth();j++)
                            if(getPixel(j,i)>=value)
                                out->pixelmap[i][j]=true;
                    break;
                case 2:
                    for(unsigned int i=0;i<getHeight();i++)
                        for(unsigned int j=0;j<getWidth();j++)
                            if(getPixel(j,i)<=value)
                                out->pixelmap[i][j]=true;
                    break;
            }
            return out;
        }
        if(newtype==COLOR){
            Color_Image *out = new Color_Image(getHeight(),getWidth());
            switch(option){
                case 0:
                    for(unsigned int i=0;i<getHeight();i++)
                        for(unsigned int j=0;j<getWidth();j++)
                            out->pixelmap[i][j]=ColorPixel(getPixel(j,i),getPixel(j,i),getPixel(j,i));
                    break;
                case 1:
                    for(unsigned int i=0;i<getHeight();i++)
                            for(unsigned int j=0;j<getWidth();j++)
                                if(getPixel(j,i)==0)
                                    out->pixelmap[i][j]=ColorPixel(0,0,0);


                    ColorPixel *looktable = new ColorPixel[66000];

                    for(unsigned int i=0;i<getHeight();i++)
                        for(unsigned int j=0;j<getWidth();j++){
                            unsigned int value=getPixel(j,i);
                            if(value!=0){
                                if(looktable[value]==ColorPixel(0,0,0)){
                                    looktable[value]=ColorPixel(rand()%255,((rand()%3)*rand())%255,(rand()+rand())%255);
                                    out->pixelmap[i][j]=looktable[value];
                                }
                                else{
                                    out->pixelmap[i][j]=looktable[value];
                                }
                            }
                            else
                                out->pixelmap[i][j]=ColorPixel(0,0,0);
                        }

                    delete [] looktable;
                    break;
            }
            return out;
        }
    }
    return 0;
}

Grey_Image* Grey_Image::greycopy(Grey_Image* imageout){
    Grey_Image* out;
    if(imageout==NULL)
        out = new Grey_Image(this->getHeight(),this->getWidth(),this->getGreyLevels());
    else
        out = imageout;

    for(unsigned int i=0;i<this->getHeight();i++)
        for(unsigned int j=0;j<this->getWidth();j++)
            out->pixelmap[i][j]=this->pixelmap[i][j];
    return out;
}

void Grey_Image::AND(Binary_Image *img){
    for(unsigned int i=0;i<this->getHeight();i++)
        for(unsigned int j=0;j<this->getWidth();j++)
            this->pixelmap[i][j]=this->pixelmap[i][j]*(unsigned int)img->pixelmap[i][j];
}


void Grey_Image::MINUS(Grey_Image *img){
     for(unsigned int i=0;i<this->getHeight();i++)
        for(unsigned int j=0;j<this->getWidth();j++)
            this->pixelmap[i][j]=this->pixelmap[i][j]-img->pixelmap[i][j];
}


void Grey_Image::ADD(Grey_Image *img){
     for(unsigned int i=0;i<this->getHeight();i++)
        for(unsigned int j=0;j<this->getWidth();j++)
            this->pixelmap[i][j]=this->pixelmap[i][j]+img->pixelmap[i][j];
}

list<Position>* Grey_Image::getListofPoints(unsigned int label){
    list<Position> *points = new list<Position>;
    for(unsigned int i=0;i<this->getHeight();i++)
        for(unsigned int j=0;j<this->getWidth();j++)
            if(this->pixelmap[i][j]==label)
                points->push_back(Position(j,i));
    return points;
}

queue<Position>* Grey_Image::getQueueofPoints(unsigned int label){
    queue<Position> *points = new queue<Position>;
    for(unsigned int i=0;i<this->getHeight();i++)
        for(unsigned int j=0;j<this->getWidth();j++)
            if(this->pixelmap[i][j]==label)
                points->push(Position(j,i));
    return points;
}

stack<Position>* Grey_Image::getStackofPoints(unsigned int label){
    stack<Position> *points = new stack<Position>;
    for(unsigned int i=0;i<this->getHeight();i++)
        for(unsigned int j=0;j<this->getWidth();j++)
            if(this->pixelmap[i][j]==label)
                points->push(Position(j,i));
    return points;
}

Position Grey_Image::getMaximum(Grey_Image *img){
    int xmax=0,ymax=0;
    unsigned int maxvalue=0;

    for(unsigned int i=0;i<img->getHeight();i++)
        for(unsigned int j=0;j<img->getWidth();j++){
            if(img->getPixel(j,i)>maxvalue){
                xmax=j;
                ymax=i;
                maxvalue=img->getPixel(j,i);
            }
        }
    return Position(xmax,ymax);
}

//////////
//Binary_Image methods
//////////

Binary_Image::Binary_Image(unsigned int height,unsigned int width){

    //set Binary_image propriety:
    setHeight(height);
    setWidth(width);
    setPixelSize(SIZE_NOT_DEFINED);
    setType(Image::BINARY);

    //Allocate pixelmap:
    pixelmap = new bool* [height];
    for(unsigned int i=0;i<height;i++)
        pixelmap[i]=new bool [width];
    if(pixelmap[height-1]==NULL)exit(13)/*Must be swapped by correct "out of memory" function/exception*/;

    //Zero pixmap:
    for(unsigned int i=0;i < height;i++)
        for(unsigned int j=0;j < width;j++)
            pixelmap[i][j]=false;

}

Binary_Image::~Binary_Image(){
    //Desallocate pixelmap:
    for(unsigned int i=0;i < (getHeight()) ;i++)
        delete[] pixelmap[i];

    delete[] pixelmap;
}

void Binary_Image::setPixel(Position position,bool value){
    if(position.isInside(-1,-1,getWidth(),getHeight()))
        pixelmap[position.y][position.x]=value;
}
void Binary_Image::setPixel(int xposition,int yposition,bool value){
    if(xposition<(int)getWidth() && yposition<(int)getHeight()
       && xposition>=0 && yposition >=0)
        pixelmap[yposition][xposition]=value;
}

bool Binary_Image::getPixel(Position position){
    if(position.isInside(-1,-1,getWidth(),getHeight()))
        return pixelmap[position.y][position.x];
    return false;
}
bool Binary_Image::getPixel(int xposition,int yposition){
    if(xposition<(int)getWidth() && yposition<(int)getHeight()
       && xposition>=0 && yposition >=0)
        return pixelmap[yposition][xposition];
    return false;
}

void Binary_Image::invert(){
    for(unsigned int i=0;i<getHeight();i++)
        for(unsigned int j=0;j<getWidth();j++){
            pixelmap[i][j]=!pixelmap[i][j];
        }
}

Image* Binary_Image::convert(ImageType newtype,int option,unsigned int value){
    if(newtype!=getType()){
        if(newtype==GREY){
            Grey_Image *out = new Grey_Image(getHeight(),getWidth(),256);
            switch(option){
                case 0:
                    for(unsigned int i=0;i<getHeight();i++)
                        for(unsigned int j=0;j<getWidth();j++)
                            if(getPixel(j,i)==true)
                                out->pixelmap[i][j]=1;
                    break;
                case 1:
                    for(unsigned int i=0;i<getHeight();i++)
                        for(unsigned int j=0;j<getWidth();j++)
                            if(getPixel(j,i)==true)
                                out->pixelmap[i][j]=255;
                    break;
            }
            return out;
        }
        if(newtype==COLOR){
            Color_Image *out = new Color_Image(getHeight(),getWidth());
            switch(option){
                case 0:
                    for(unsigned int i=0;i<getHeight();i++)
                            for(unsigned int j=0;j<getWidth();j++)
                                if(getPixel(j,i)==true)
                                    out->pixelmap[i][j]=ColorPixel(0,0,0);
                                else
                                    out->pixelmap[i][j]=ColorPixel(255,255,255);
                    break;
            }
            return out;
        }
    }
    return 0;
}

void Binary_Image::AND(Binary_Image *img){
    for(unsigned int i=0;i<this->getHeight();i++)
        for(unsigned int j=0;j<this->getWidth();j++)
            this->pixelmap[i][j]=this->pixelmap[i][j]&&img->pixelmap[i][j];
}

void Binary_Image::OR(Binary_Image *img){
    for(unsigned int i=0;i<this->getHeight();i++)
        for(unsigned int j=0;j<this->getWidth();j++)
            this->pixelmap[i][j]=this->pixelmap[i][j]||img->pixelmap[i][j];
}

void Binary_Image::DIFFERENCE(Binary_Image *img){
    for(unsigned int i=0;i<this->getHeight();i++)
        for(unsigned int j=0;j<this->getWidth();j++)
            if(this->pixelmap[i][j]!=0)this->pixelmap[i][j]=this->pixelmap[i][j]-img->pixelmap[i][j];

}

Binary_Image* Binary_Image::binarycopy(Binary_Image* imageout){
    Binary_Image* out;
    if(imageout==NULL)
        out = new Binary_Image(this->getHeight(),this->getWidth());
    else
        out = imageout;

    for(unsigned int i=0;i<this->getHeight();i++)
        for(unsigned int j=0;j<this->getWidth();j++)
            out->pixelmap[i][j]=this->pixelmap[i][j];
    return out;
}

list<Position>* Binary_Image::getListofPoints(bool type){
    list<Position> *points = new list<Position>;
    for(unsigned int i=0;i<this->getHeight();i++)
        for(unsigned int j=0;j<this->getWidth();j++)
            if(this->pixelmap[i][j]==type)
                points->push_back(Position(j,i));
    return points;
}

queue<Position>* Binary_Image::getQueueofPoints(bool type){
    queue<Position> *points = new queue<Position>;
    for(unsigned int i=0;i<this->getHeight();i++)
        for(unsigned int j=0;j<this->getWidth();j++)
            if(this->pixelmap[i][j]==type)
                points->push(Position(j,i));
    return points;
}

stack<Position>* Binary_Image::getStackofPoints(bool type){
    stack<Position> *points = new stack<Position>;
    for(unsigned int i=0;i<this->getHeight();i++)
        for(unsigned int j=0;j<this->getWidth();j++)
            if(this->pixelmap[i][j]==type)
                points->push(Position(j,i));
    return points;
}

void Binary_Image::drawCircle(int centerx,int centery,unsigned int radii){
    unsigned int xk,xkp1;
    unsigned int yk,ykp1;
    float pk,pkp1;

    xk=0;
    yk=radii;
    pk=1-radii;

    while(yk>=xk){
        for(unsigned int i=yk;i>xk;i--){
            setPixel(centerx+xk,centery+i,true);
            setPixel(centerx+i,centery+xk,true);
            setPixel(centerx+i,centery-xk,true);
            setPixel(centerx+xk,centery-i,true);
            setPixel(centerx-xk,centery-i,true);
            setPixel(centerx-i,centery-xk,true);
            setPixel(centerx-i,centery+xk,true);
            setPixel(centerx-xk,centery+i,true);
        }
        setPixel(centerx+xk,centery+xk,true);
        setPixel(centerx-xk,centery+xk,true);
        setPixel(centerx+xk,centery-xk,true);
        setPixel(centerx-xk,centery-xk,true);
        if(pk<0){
            xkp1=xk+1;
            ykp1=yk;
        }else{
            xkp1=xk+1;
            ykp1=yk-1;
        }
        pkp1=(xkp1*xkp1)+(ykp1+1.0/2)*(ykp1+1.0/2)-radii*radii;
        xk=xkp1;
        yk=ykp1;
        pk=pkp1;
    }
}

bool Binary_Image::operator==(Binary_Image* img){
    if(this->getHeight()!=img->getHeight())
        return false;
    if(this->getWidth()!=img->getWidth())
        return false;

    for(unsigned int i=0;i<this->getHeight();i++)
        for(unsigned int j=0;j<this->getWidth();j++){
            if(this->getPixel(j,i)!=img->getPixel(j,i))
                return false;
        }

    return true;

}

bool Binary_Image::operator!=(Binary_Image* img){
    if(this->getHeight()!=img->getHeight())
        return true;
    if(this->getWidth()!=img->getWidth())
        return true;

    for(unsigned int i=0;i<this->getHeight();i++)
        for(unsigned int j=0;j<this->getWidth();j++){
            if(this->getPixel(j,i)!=img->getPixel(j,i))
                return true;
        }

    return false;

}

//ColorPixel

ColorPixel::ColorPixel(unsigned char red,unsigned char green,unsigned char blue){
    this->red=red;
    this->green=green;
    this->blue=blue;
}
ColorPixel::~ColorPixel(){}

unsigned char ColorPixel::getRed(){
    return red;
}
unsigned char ColorPixel::getGreen(){
    return green;
}
unsigned char ColorPixel::getBlue(){
    return blue;
}

void ColorPixel::setRed(unsigned char value){
    red=value;
}
void ColorPixel::setGreen(unsigned char value){
    green=value;
}
void ColorPixel::getBlue(unsigned char value){
    blue=value;
}

bool ColorPixel::operator!=(ColorPixel a){
    if(a.getRed()==this->getRed())
        if(a.getGreen()==this->getGreen())
            if(a.getBlue()==this->getBlue()) return false;
            else return true;
        else return true;
    else return true;
}

bool ColorPixel::operator==(ColorPixel a){
    if(a.getRed()==this->getRed())
        if(a.getGreen()==this->getGreen())
            if(a.getBlue()==this->getBlue()) return true;
            else return false;
        else return false;
    else return false;
}


//Color image functions

Color_Image::Color_Image(unsigned int height,unsigned int width){

    //set Grey_image propriety:
    setHeight(height);
    setWidth(width);
    setPixelSize(SIZE_NOT_DEFINED);
    setType(COLOR);

    //Allocate pixelmap:
    pixelmap = new ColorPixel*[height];
    for(unsigned int i=0;i<height;i++)
        pixelmap[i]=new ColorPixel[width];
    if(pixelmap[height-1]==NULL)exit(13)/*Must be swapped by correct "out of memory" function/exception*/;

    //Zero pixmap:
    for(unsigned int i=0;i < height;i++)
        for(unsigned int j=0;j < width;j++)
            pixelmap[i][j]=ColorPixel(0,0,0);


}

Color_Image::~Color_Image(){
    //Desallocate pixelmap:
    for(unsigned int i=0;i < getHeight() ;i++)
        delete[] pixelmap[i];

    delete[] pixelmap;
}

void Color_Image::setPixel(Position position,ColorPixel value){
    if(position.isInside(-1,-1,getWidth(),getHeight()))
        pixelmap[position.y][position.x]=value;
}


ColorPixel Color_Image::getPixel(Position position){
   if(position.isInside(-1,-1,getWidth(),getHeight()))
        return pixelmap[position.y][position.x];
    else
        return 0;
}

ColorPixel Color_Image::getPixel(int xposition,int yposition){
    if(xposition<(int)getWidth() && yposition<(int)getHeight()
       && xposition>=0 && yposition >=0)
        return pixelmap[yposition][xposition];
    else
        return 0;
}


void Color_Image::invert(){
    for(unsigned int i=0;i<getHeight();i++)
        for(unsigned int j=0;j<getWidth();j++){
            pixelmap[i][j]=ColorPixel(255-pixelmap[i][j].red,255-pixelmap[i][j].green,255-pixelmap[i][j].blue);
        }
}

Image* Color_Image::convert(ImageType newtype,int option,unsigned int value){
    if(newtype!=getType()){
        if(newtype==BINARY){
            Binary_Image *out = new Binary_Image(getHeight(),getWidth());
            switch(option){
                case 0:
                    for(unsigned int i=0;i<getHeight();i++)
                        for(unsigned int j=0;j<getWidth();j++){
                            if(getPixel(j,i)==ColorPixel(0,0,0))
                                out->pixelmap[i][j]=false;
                            else
                                out->pixelmap[i][j]=true;
                        }
                    break;
                case 1:
                    for(unsigned int i=0;i<getHeight();i++)
                        for(unsigned int j=0;j<getWidth();j++){
                            if(getPixel(j,i).getRed()==0)
                                out->pixelmap[i][j]=false;
                            else
                                out->pixelmap[i][j]=true;
                        }
                    break;
                case 2:
                    for(unsigned int i=0;i<getHeight();i++)
                        for(unsigned int j=0;j<getWidth();j++){
                            if(getPixel(j,i).getGreen()==0)
                                out->pixelmap[i][j]=false;
                            else
                                out->pixelmap[i][j]=true;
                        }
                    break;
                case 3:
                    for(unsigned int i=0;i<getHeight();i++)
                        for(unsigned int j=0;j<getWidth();j++){
                            if(getPixel(j,i).getBlue()==0)
                                out->pixelmap[i][j]=false;
                            else
                                out->pixelmap[i][j]=true;
                        }
                    break;
            }
            return out;
        }
        if(newtype==GREY){
            Grey_Image *out = new Grey_Image(getHeight(),getWidth(),256);
            switch(option){
                case 0:
                    for(unsigned int i=0;i<getHeight();i++)
                        for(unsigned int j=0;j<getWidth();j++){
                                out->pixelmap[i][j]=(getPixel(j,i).getRed()+getPixel(j,i).getGreen()+getPixel(j,i).getBlue())/3;
                        }
                    break;
                case 1:
                    for(unsigned int i=0;i<getHeight();i++)
                        for(unsigned int j=0;j<getWidth();j++){
                                out->pixelmap[i][j]=getPixel(j,i).getRed();
                        }
                    break;
                case 2:
                    for(unsigned int i=0;i<getHeight();i++)
                        for(unsigned int j=0;j<getWidth();j++){
                                out->pixelmap[i][j]=getPixel(j,i).getGreen();
                        }
                    break;
                case 3:
                    for(unsigned int i=0;i<getHeight();i++)
                        for(unsigned int j=0;j<getWidth();j++){
                                out->pixelmap[i][j]=getPixel(j,i).getBlue();
                        }
                    break;
            }
            return out;
        }
    }
    return 0;
}

Color_Image* Color_Image::colorcopy(Color_Image* imageout){
    Color_Image* out;
    if(imageout==NULL)
        out = new Color_Image(this->getHeight(),this->getWidth());
    else
        out = imageout;

    for(unsigned int i=0;i<this->getHeight();i++)
        for(unsigned int j=0;j<this->getWidth();j++)
            out->pixelmap[i][j]=this->pixelmap[i][j];
    return out;
}

